using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure")]
    [XmlRoot(Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure", IsNullable = false)]
    public class Item : BaseItem<Folder>
    {
        private File _file;
        private bool _approve;

        /// <remarks/>
        [XmlAttribute]
        public bool Approve
        {
            get
            {
                return _approve || _file != null && _file.FileAction == FileActionType.Approve;
            }
            set
            {
                _approve = value;
            }
        }

        [XmlElement]
        public File File
        {
            get { return _file; }
            set
            {
                _file = value;
                _file.Item = this;
            }
        }

        /// <summary>
        /// Adds, Replaces, Deletes, Modifies or Moves a item in a given folder
        /// </summary>
        /// <param name="itemDefinition">Xml object for an item</param>
        /// <param name="item">A item based on the itemDefinition or null</param>
        /// <param name="parentFolder">The parent folder where to import the item</param>
        protected internal void ImportItem(Item itemDefinition, SPListItem item, SPFolder parentFolder)
        {
            Trace.WriteLine("Try to import item '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportItem");

            SPFile file;

            if (item == null)
            {
                if (itemDefinition.Action == ActionType.Add || itemDefinition.Action == ActionType.Replace)
                    item = CreateOrReplaceItem(itemDefinition, parentFolder);
                else
                    return;
            }
            else
            {
                if (itemDefinition.Action == ActionType.Replace && item.File != null)
                {
                    file = item.File;

                    if (file != null)
                    {
                        if (GetUndoCheckoutParameter(itemDefinition.Parent) && file.CheckedOutBy != null && file.CheckedOutBy.LoginName != ContentStructureImporter.FeatureWeb.CurrentUser.LoginName)
                            file.UndoCheckOut();

                        if (file.CheckedOutBy == null)
                            file.CheckOut();

                        item = CreateOrReplaceItem(itemDefinition, parentFolder);
                    }
                }
                else if (itemDefinition.Action == ActionType.Delete)
                {
                    item.Delete();
                    return;
                }
            }

            if (item == null)
                return;

            file = item.File;
            List<WebPart> webPartDefinitions = itemDefinition.File != null ? itemDefinition.File.WebParts : null;

            if (file != null &&
                webPartDefinitions != null &&
                webPartDefinitions.Count > 0)
            {
                var webPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared);

                if (itemDefinition.File.RemoveAllWebParts)
                {
                    for (int index = 0; index < webPartManager.WebParts.Count; )
                    {
                        webPartManager.DeleteWebPart(webPartManager.WebParts[index]);
                    }
                }

                CreateContext(ContentStructureImporter.FeatureWeb.Url + "/_layouts/ManageFeatures.aspx?Scope=Site");

                for (int index = 0; index < webPartDefinitions.Count; index++)
                {
                    WebPart webPartDefinition = webPartDefinitions[index];
                    string webPartName = NamedElement.ResolveResource(webPartDefinition.Title,
                                                                      parentFolder.ParentWeb.Locale);
                    System.Web.UI.WebControls.WebParts.WebPart webPart = webPartManager.WebParts[webPartName];
                    new WebPart().ImportWebPart(webPartDefinition, webPart, webPartManager);
                }

                ClearContext();
                
                file.Update();
            }

            if (itemDefinition.Action != ActionType.None &&
                itemDefinition.Action <= ActionType.Modify)
            {
                ModifyItemProperties(itemDefinition, item);
                if (file != null)
                {
                    File.ModifyFile(itemDefinition, file);
                }
            }

            if (file != null && itemDefinition.File != null)
            {
                if (itemDefinition.File.FileAction >= FileActionType.CheckIn &&
                    file.Level == SPFileLevel.Checkout)
                {
                    file.CheckIn("Automatic checkin through: SDC ContentStructure-Deployment");
                }

                if (itemDefinition.File.FileAction >= FileActionType.Publish)
                {
                    if (file.Level == SPFileLevel.Draft)
                        file.Publish("Automatic publishing through: SDC ContentStructure-Deployment");
                }

                ContentStructureImporter._fileExtensionActions.Execute(itemDefinition, file);
            }

            if (itemDefinition.Approve)
                ApproveItem(item);

            Permissions.SetPermissions(itemDefinition, item, item.ParentList.ParentWeb);

            Trace.WriteLine("Item '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "' imported.", "ContentStructureImporter:ImportItem");
        }

        /// <summary>
        /// Creates or replaces an item in a given folder
        /// </summary>
        /// <param name="itemDefinition">Xml object for an item</param>
        /// <param name="parentFolder">The parent folder where to create or replace the item</param>
        /// <returns></returns>
        private SPListItem CreateOrReplaceItem(Item itemDefinition, SPFolder parentFolder)
        {
            Trace.WriteLine("Try to create or replace item '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:CreateOrReplaceItem");

            FileStream fileStream = null;
            string filePath = null;

            try
            {
                string itemPath = NamedElement.ResolveResource(itemDefinition.Path, parentFolder.ParentWeb.Locale);
                string itemName = NamedElement.ResolveResource(itemDefinition.Title, parentFolder.ParentWeb.Locale);
                string itemUrl = NamedElement.ResolveResource(itemDefinition.Url, parentFolder.ParentWeb.Locale);
                filePath = GetFeatureRootedPath(itemPath);

                if (itemDefinition.File != null)
                {
                    fileStream = System.IO.File.OpenRead(filePath);

                    SPFile file = parentFolder.Files.Add(itemUrl ?? itemName, fileStream,
                                                         itemDefinition.Action == ActionType.Replace);

                    return file.Item;
                }
                else
                {
                    if (string.IsNullOrEmpty(itemUrl))
                        itemUrl = parentFolder.ServerRelativeUrl.TrimEnd('/');

                    var query = new SPQuery();
                    query.Query = "<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + itemDefinition.Title + "</Value></Eq></Where><OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>";
                    query.Folder = parentFolder;
                    var list = parentFolder.ParentWeb.Lists[parentFolder.ParentListId];
                    var listItems = list.GetItems(query);

                    if (itemDefinition.Action == ActionType.Replace)
                    {
                        for (var i = listItems.Count - 1; i >= 0; i--)
                            listItems[i].Delete();
                    }
                    else if (itemDefinition.Action == ActionType.Modify && listItems.Count > 0)
                        return listItems[0];
                    else if (listItems.Count > 0)
                        return null;
                    return parentFolder.ParentWeb.Lists[parentFolder.ParentListId].Items.Add(itemUrl, SPFileSystemObjectType.File, itemName);
                }
            }
            catch (Exception ex)
            {
                throw new ContentStructureImporterException(
                    "Error while importing file '" + filePath + "'", ex);
            }
            finally
            {
                if (fileStream != null)
                    fileStream.Close();

                Trace.WriteLine("item '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "' created or replaced.", "ContentStructureImporter:CreateOrReplaceItem");
            }
        }

        /// <summary>
        /// Modifies the properties of an item
        /// </summary>
        /// <param name="itemDefinition">Xml object for an IBaseItem</param>
        /// <param name="item">The list item that properties should changed</param>
        protected static internal void ModifyItemProperties(IBaseItem itemDefinition, SPListItem item)
        {
            Trace.WriteLine("Try to modify item properties '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ModifyItemProperties");

            if (item == null)
                return;

            Hashtable properties = itemDefinition.GetProperiesAsHashtable(item.Web.Locale);

            Item currentItem = itemDefinition as Item;

            if (item.Fields.ContainsField("Title") && item.Title != itemDefinition.Title && currentItem != null && currentItem.File != null &&
                currentItem.File.OverwriteFileTitleWithItemTitle)
            {
                if (!properties.ContainsKey("Title"))
                    properties["Title"] = itemDefinition.Title;
            }

            if (properties.Count == 0)
                return;

            if (properties.ContainsKey("ContentTypeId"))
            {
                var contentTypeId =
                    item.ParentList.ContentTypes.BestMatch(new SPContentTypeId(properties["ContentTypeId"].ToString()));
                //item.ContentType.Name = item.ParentList.ContentTypes[contentTypeId].Name;
                item["ContentTypeId"] = contentTypeId;
            }
            foreach (string propertyKey in properties.Keys)
            {
                string key = propertyKey;
                object value = properties[propertyKey];

                if ((item.ContentType != null && item.ContentType.Fields.ContainsField(key)) ||
                    (item.ContentType == null && item.Fields.ContainsField(key)))
                {
                    if (value != null && value.GetType().IsEnum)
                        item[key] = (int)value;
                    else
                        item[key] = value;
                }
            }

            item.Update();

            Trace.WriteLine("Item properties '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "' modified.", "ContentStructureImporter:ModifyItemProperties");
        }

        /// <summary>
        /// Changes the status a given item to approve
        /// </summary>
        /// <param name="item"></param>
        protected internal void ApproveItem(SPListItem item)
        {
            String title = item.Fields.ContainsField("Title") ? item.Title : item.Name;

            Trace.WriteLine("Try to approve item '" + title + "' with url '" + item.Url + "'.", "ContentStructureImporter:ApproveItem");

            if (item != null &&
                item.ModerationInformation != null &&
                item.ModerationInformation.Status == SPModerationStatusType.Pending)
            {
                if (item.File == null)
                {
                    item.ModerationInformation.Status = SPModerationStatusType.Approved;
                    item.ModerationInformation.Comment = "Automatic approval through: SDC ContentStructure-Deployment";
                }
                else
                    item.File.Approve("Automatic approval through: SDC ContentStructure-Deployment");
            }

            Trace.WriteLine("Item '" + title + "' with url '" + item.Url + "' approved.", "ContentStructureImporter:ApproveItem");
        }

        private static bool GetUndoCheckoutParameter(IBaseItem item)
        {
            if (item is List)
                return ((List)item).UndoFileCheckout;

            return false;
        }
    }
}